import Konva from "konva";
import { Draw } from "../Draw/Draw";
import { IKeyMap } from "../Interface/Pptx";
import { getUniqueId, isEmpty, nextTick } from "../Utils";
import { colorMatchTemp } from "../Template/DialogTemp";
import { COLOR_MATCH_ARRAY } from "../Config/color-match-array";
import { ShapeAnimationEnum } from "../Enumerate/ShapeAnimation";
import { animationListTemp } from "../Template/ShapeSettingsTemp";
import { color_getHexColor, getImageSource } from "../Utils/konva";
import { animationTemp, designTemp } from "../Template/ShapeSettingsTemp";
import { optionTemp, ShapeSettingsTemp } from "../Template/ShapeSettingsTemp";
import { ANIMATION_NAME_MAP } from "../Config/animation-name-map";

/** 定义该组件常用类型 */
type HSE = HTMLSelectElement;
type HIE = HTMLInputElement;
type HE = HTMLElement;

type NOFHSE = NodeListOf<HTMLSelectElement>;
type NOFHIE = NodeListOf<HTMLInputElement>;
type NOFHE = NodeListOf<HTMLElement>;

/**
 * 设计、属性、动画 - 公共抽屉窗口
 *  实现思路是通过获取
 *    data-operator-button 绑定事件
 *    data-operator-input 绑定事件
 *    data-operator-select 绑定事件
 *  来识别唯一的事件源，执行响应操作
 */
export class ShapeSettings {
  private draw: Draw;

  // 记录上一次位置，从上次位置打开
  private lstx: number = 0;
  private lsty: number = 0;

  constructor(draw: Draw) {
    this.draw = draw;
  }

  /**
   * 打开配置面板
   * @param index 菜单索引 - 设计 0 属性 1 动画 2
   * @returns
   */
  public open(index?: 0 | 1 | 2) {
    if (index && index >= 3)
      return this.draw.getMessage().warn("属性配置面板参数错误");

    const root = this.draw.getRootBox();
    const settingsClassName = "konva-root-shape-settings";
    const box = root.querySelector(`.${settingsClassName}`);
    if (box) return; // 如果已经存在

    // 创建可关闭蒙版
    this.draw.getMenuMask().createMask();

    // 创建属性设置面板
    const settingBox = document.createElement("div");
    settingBox.className = settingsClassName;
    settingBox.style.transform = `translate(${this.lstx}px, ${this.lsty}px)`; // 恢复上次位置
    settingBox.innerHTML = ShapeSettingsTemp;

    // 添加到 root 上
    root.appendChild(settingBox);

    // 1. 初始化拖拽事件
    this.addDragboxEvent(settingBox);

    // 2. 渲染合适的模板
    const renderTemplate = (index: number) => {
      const root = this.draw.getRootBox();

      const tempMap = [designTemp, optionTemp, animationTemp];

      const contentSelector = ".konva-root-shape-settings-menu-content";
      const contentBox = <HTMLElement>root.querySelector(contentSelector)!;
      contentBox.innerHTML = tempMap[index];

      // 渲染模板完成后，需要进行事件注册
      const eventMap = [
        this.initDesignEvent.bind(this),
        this.initSettingEvent.bind(this),
        this.initAnimationEvent.bind(this),
      ];
      if (eventMap[index]) eventMap[index]();
    };

    // 3. 遍历 设计、属性、动画 三个菜单，处理激活样式，并且绑定点击事件
    type NLS = NodeListOf<HTMLSpanElement>;
    const menuSelector = ".konva-root-shape-settings-menu-tabs span";
    const menuList = <NLS>root.querySelectorAll(menuSelector);

    menuList.forEach((span, i) => {
      // 如果 index 不存在，默认激活 第一个菜单
      if (!isEmpty(index) && index === i) span.classList.add("active");
      else if (isEmpty(index) && !i) span.classList.add("active");

      // 注册点击事件
      span.addEventListener("click", () => {
        menuList.forEach((span) => span.classList.remove("active"));
        span.classList.add("active");
        renderTemplate(i);
      });
    });

    // 渲染合适的模板
    renderTemplate(index || 0);
  }

  /** 初始化菜单拖拽事件 */
  private addDragboxEvent(settingBox: HTMLElement) {
    let sx = 0;
    let sy = 0;
    let tx = 0;
    let ty = 0;

    const dragBox = <HTMLElement>settingBox.querySelector(".drag-box");

    dragBox.addEventListener("mousedown", mousedown);
    const mouseup = () => document.removeEventListener("mousemove", mousemove);

    function mousedown(e: MouseEvent) {
      // 记录初始位置
      sx = e.clientX;
      sy = e.clientY;
      const transform = settingBox.style.transform || "translate(0px,0px)";
      [tx, ty] = transform
        .replace(/translate|\(|\)|px/g, "")
        .split(",")
        .map((i) => Number(i));

      document.addEventListener("mousemove", mousemove);
      document.addEventListener("mouseup", mouseup);
    }
    const mousemove = (e: MouseEvent) => {
      // 计算位置偏差
      const offsetx = e.clientX - sx;
      const offsety = e.clientY - sy;
      // 取原来的位置，进行合计
      this.lstx = tx + offsetx;
      this.lsty = ty + offsety;
      settingBox.style.transform = `translate(${this.lstx}px, ${this.lsty}px)`;
    };
  }

  /**
   * 初始化设计部分事件
   */
  private initDesignEvent() {
    // 1. 背景颜色与当前图层的背景相关
    const layer = this.draw.getLayer();
    if (!layer) return;

    // 颜色转两次是确保 #fff ===> #ffffff
    const bgGroup = layer.findOne(".bgGroup");
    const color = color_getHexColor(bgGroup?.attrs.fill);
    const selector = 'data-operator-input="fill-color"';
    const fillColorInput = <HIE>this.getDom(`[${selector}]`);
    fillColorInput.value = color;

    /**
     * 开始初始化配色方案 .colorMatch
     *  通过 Config/Theme colorMatch 循环生成 按钮，并添加 data-operator-color-match 实现点击事件识别
     */
    const colorMatchBox = this.draw
      .getRootBox()
      .querySelector(".konva-root-shape-settings .colorMatch");

    // 获取当前配色方案
    const theme = this.draw.getColorScheme();
    const currentColorMatch = theme.getColorMatch();

    for (const key in COLOR_MATCH_ARRAY) {
      if (Object.prototype.hasOwnProperty.call(COLOR_MATCH_ARRAY, key)) {
        const colorMatchItem = COLOR_MATCH_ARRAY[key];

        // 要文字颜色、填充颜色、表头、前四系列色即可
        const {
          slideFillColor,
          nodeTextColor,
          nodeFillColor,
          tableHeaderFillColor,
          seriesColor,
        } = colorMatchItem;

        const match = document.createElement("div");
        match.setAttribute("data-operator-color-match", key);
        match.className = "colorMatchItem";
        // 激活当前选中的 配色方案
        if (currentColorMatch === colorMatchItem) match.classList.add("active");
        match.style.backgroundColor = slideFillColor!;
        match.innerHTML = `
         <span style="color:${nodeTextColor}">Aa</span>
         <div style="background-color:${nodeFillColor}"></div>
         <div style="background-color:${tableHeaderFillColor}"></div>
         <div style="background-color:${seriesColor![0]}"></div>
         <div style="background-color:${seriesColor![2]}"></div>
         <div style="background-color:${seriesColor![4]}"></div>
          `;
        colorMatchBox?.appendChild(match);
      }
    }

    // 初始化 配色方案 事件
    const colorMatchList = <NOFHE>this.getDoms("[data-operator-color-match]");
    colorMatchList.forEach((item) => {
      const colorMatch = <string>item.dataset.operatorColorMatch!;
      item.addEventListener("click", () => {
        if (colorMatch === currentColorMatch) return;
        // 处理active
        colorMatchList.forEach((item) => item.classList.remove("active"));
        item.classList.add("active");
        theme.setColorMatch(colorMatch);
        // 广播协同配色方案
        this.draw.broadcast({
          type: "_colorMatch",
          payload: colorMatch,
        });
      });
    });

    // 所有的事件Map
    const eventMap: IKeyMap<string, (e: Event) => void> = {
      "fill-type": this.toggleFillType.bind(this), // 背景填充类型：纯色/背景 切换
      "image-picker": this.imagePicker.bind(this), // 图片选择器
      "apply-all": this.applyAll.bind(this),
      "fill-color": this.fillColorInputChange.bind(this),
      "gridline-open": this.toggleGridLine.bind(this),
      "gridline-color": this.gridLineColorChange.bind(this),
      zdycolormatch: this.zdyColorMatch.bind(this),
    };

    // 所有的 input
    const inputlist = <NOFHSE>this.getDoms("[data-operator-input]");
    inputlist.forEach((input) => {
      const operator = input.dataset.operatorInput!;
      input.addEventListener("input", eventMap[operator]);
    });
    // 所有的 select
    const selectlist = <NOFHSE>this.getDoms("[data-operator-select]");
    selectlist.forEach((select) => {
      const operator = select.dataset.operatorSelect!;
      select.addEventListener("change", eventMap[operator]);
    });

    // 所有的可点击按钮
    const btns = <NOFHE>this.getDoms("[data-operator-button]");
    btns.forEach((btn) => {
      const operator = btn.dataset.operatorButton!;
      btn.addEventListener("click", eventMap[operator]);
    });
  }

  /**
   * 初始化属性部分事件
   */
  private initSettingEvent() {
    // 1. 判断当前是否有元素被选中
    const selected = this.draw.getKonvaGraph().getSelected();
    const command = this.draw.getCommand();

    // 获取所有的输入框 、所有的select
    const inputlist = <NOFHIE>this.getDoms("[data-operator-input]");
    const selectlist = <NOFHSE>this.getDoms("[data-operator-select]");
    const buttonlist = <NOFHSE>this.getDoms("[data-operator-button]");

    /** 取对应的 input */
    function getInput(type: string) {
      const inputArray = [...inputlist];
      return inputArray.find((i) => i.dataset.operatorInput === type)!;
    }

    function getSelect(type: string) {
      const selectArray = [...selectlist];
      return selectArray.find((i) => i.dataset.operatorSelect === type)!;
    }

    // 解析是否有 Line 被选中
    const lineSelected = this.draw.getKonvaGraph().getLineSelected();

    if (lineSelected.length) {
      inputlist.forEach((i) => (i.disabled = true));
      selectlist.forEach((s) => (s.disabled = true));
      // 单独处理 Line 的属性
      // 解析 Line 的属性
      const { strokeWidth, stroke, pointerLength } = lineSelected[0].attrs;

      const lineWidthInput = getInput("line-width");
      lineWidthInput.disabled = false;
      lineWidthInput.value = strokeWidth.toString();
      lineWidthInput.addEventListener("input", this.lineWidthChange.bind(this));

      const lineColorInput = getInput("line-color");
      lineColorInput.value = color_getHexColor(stroke);
      lineColorInput.disabled = false;
      lineColorInput.addEventListener("input", this.lineColorChange.bind(this));

      const showPointerInput = getInput("show-pointer");
      showPointerInput.checked = Boolean(pointerLength);
      showPointerInput.disabled = false;
      showPointerInput.addEventListener(
        "change",
        this.showPointerChange.bind(this)
      );

      return;
    }

    // 如果没有元素被选中，则添加 disabled
    if (!selected.length) {
      inputlist.forEach((i) => (i.disabled = true));
      selectlist.forEach((s) => (s.disabled = true));
      return;
    }

    /**
     * 不然根据用户选中的元素 0 进行基本属性配置，存在多选，以第一个为主
     *  1. group 是base-group，只有 x y width height 属性可用
     *  2. 取其 group[0] 是图形属性 fill stroke strokeWidth
     *  3. 如果是文本，则需要添加 text 属性
     *  4. 先初始化页面
     *  5. 后监听事件设置属性（属性设置 是所有选中的元素都跟 selected[0] 同步）
     *  6. 在处理宽高时，注意 scaleX scaleY 缩放比例,而不是修改 width height
     */
    const group = <Konva.Group>selected[0];
    const { x, y, realHeight, realWidth, width, height } = group.attrs;
    const shape = group.children[0];
    const { fill, stroke, strokeWidth, dash, rotation } = shape.attrs;

    // 判断是否存在文本节点
    const text = group.findOne("Text");
    if (text) {
      // 取 text 属性
      const { text: textValue, fontSize, fill: textFill } = text.attrs;

      const textInput = getInput("text");
      textInput.value = textValue || "";

      const textColorInput = getInput("text-color");
      textColorInput.value = color_getHexColor(textFill) || "#000000";

      const textFontSizeInput = getInput("text-fontsize");
      textFontSizeInput.value = fontSize || 16;
    } else {
      // 禁止文本区域编辑
      const textInput = getInput("text");
      textInput.disabled = true;
      const textColorInput = getInput("text-color");
      textColorInput.disabled = true;
      const textFontSizeInput = getInput("text-fontsize");
      textFontSizeInput.disabled = true;
    }

    /** 根据上诉的数据，初始化页面 */
    const widthInput = getInput("width");
    widthInput.value = Number(realWidth || width).toFixed(2);

    const heightInput = getInput("height");
    heightInput.value = Number(realHeight || height).toFixed(2);

    const leftInput = getInput("left");
    leftInput.value = Number(x).toFixed(2);

    const topInput = getInput("top");
    topInput.value = Number(y).toFixed(2);

    const rotateInput = getInput("rotate");
    rotateInput.value = Number(rotation || 0).toFixed(2);

    const fillInput = getInput("fill");
    fillInput.value = color_getHexColor(fill) || "#FFE0C0";

    /** 边框 */
    const dashSelect = getSelect("stroke-type");
    if (dash) dashSelect.value = "dash";
    else dashSelect.value = "solid";

    const strokeInput = getInput("stroke-color");
    strokeInput.value = color_getHexColor(stroke) || "#000000";

    const strokeWidthInput = getInput("stroke-width");
    strokeWidthInput.value = Number(strokeWidth || 1).toFixed(2);

    // 是否开启边框
    const strokeOpenInput = getInput("stroke-open");
    function strokeOpenChecked() {
      dashSelect.disabled = false;
      strokeInput.disabled = false;
      strokeWidthInput.disabled = false;
    }
    function strokeOpenUnChecked() {
      dashSelect.disabled = true;
      strokeInput.disabled = true;
      strokeWidthInput.disabled = true;
    }

    if (stroke && stroke !== "transparent") {
      strokeOpenInput.checked = true;
      strokeOpenChecked();
    } else {
      strokeOpenInput.checked = false;
      strokeOpenUnChecked();
    }

    /** 开始处理事件监听 */
    const triggerStokeOpen = (e: Event) => {
      // 是否启用边框
      if ((<HIE>e.target).checked) {
        const stroke = color_getHexColor(strokeInput.value);
        const strokeWidth = Number(strokeWidthInput.value);
        command.executeUpdateShapeOptions({ stroke, strokeWidth });
        return strokeOpenChecked();
      }
      strokeOpenUnChecked();
      command.executeUpdateShapeOptions({ hiddenStroke: true });
    };

    const eventMap: IKeyMap<string, (e: Event) => void> = {
      width: this.widthChange.bind(this),
      height: this.heightChange.bind(this),
      left: this.leftChange.bind(this),
      top: this.topChange.bind(this),
      fill: this.fillChange.bind(this),
      "stroke-color": this.strokeColorChange.bind(this),
      "stroke-width": this.strokeWidthChange.bind(this),
      text: this.textChange.bind(this),
      "text-color": this.textColorChange.bind(this),
      "text-fontsize": this.textFontSizeChange.bind(this),
      "stroke-type": this.strokeTypeChange.bind(this),
      leveltop: command.executeTop,
      bottom: command.executeBottom,
      holdup: command.executeHoldUp,
      putdown: command.executePutDown,
      horizontleft: command.executeHorizontLeft,
      horizoncenter: command.executeHorizonCenter,
      horizonright: command.executeHorizonRight,
      verticaltop: command.executeVerticalTop,
      verticalcenter: command.executeVerticalCenter,
      verticalbottom: command.executeVerticalBottom,
      "stroke-open": triggerStokeOpen,
    };

    inputlist.forEach((input) => {
      const inputType = input.dataset.operatorInput!;
      input.addEventListener("input", eventMap[inputType]);
    });

    selectlist.forEach((select) => {
      const operator = select.dataset.operatorSelect!;
      select.addEventListener("change", eventMap[operator]);
    });

    buttonlist.forEach((button) => {
      const operator = button.dataset.operatorButton!;
      button.addEventListener("click", eventMap[operator]);
    });
  }

  /**
   * 初始化动画部分事件
   */
  private initAnimationEvent() {
    // 有没有元素被选中
    const selected = this.draw.getKonvaGraph().getSelected();

    // 获取所有的输入框 、所有的select
    const inputlist = <NOFHIE>this.getDoms("[data-operator-input]");
    const selectlist = <NOFHSE>this.getDoms("[data-operator-select]");
    const buttonlist = <NOFHSE>this.getDoms("[data-operator-button]");

    function getButton(type: string) {
      const buttonArray = [...buttonlist];
      return buttonArray.find((i) => i.dataset.operatorButton === type)!;
    }
    function getSelect(type: string) {
      const selectArray = [...selectlist];
      return selectArray.find((i) => i.dataset.operatorSelect === type)!;
    }
    function getInput(type: string) {
      const inputArray = [...inputlist];
      return inputArray.find((i) => i.dataset.operatorInput === type)!;
    }

    // 添加动画按钮
    const addAnimation = getButton("add-animation");
    // 触发方式
    const triggerSelect = getSelect("trigger-type");
    // 持续时间
    const durationInput = getInput("animation-duration");
    // 播放
    const playBtn = getButton("animation-apply");
    // 预览
    const previewBtn = getButton("animation-preview");

    if (!selected.length) {
      addAnimation.style.cursor = "not-allowed";
      addAnimation.innerText = "请选择元素后再添加";
    } else {
      // 不然给添加动画按钮注册事件 - 打开动画列表
      addAnimation.addEventListener("click", this.onAddAnimation.bind(this));
    }

    // 触发方式/持续时间/播放/预览 与动画应用列表有关，而非是否有元素选中
    triggerSelect.disabled = true;
    durationInput.disabled = true;

    // 可能默认就有动画列表，需要先更新
    this.updateAnimationList();

    // 初始化事件
    const root = this.draw.getRootBox();
    const box = root.querySelector(".animation-applylist");
    box?.addEventListener("click", this.closeAnimationList.bind(this));

    // 触发方式
    triggerSelect.addEventListener("change", this.onTriggerSelect.bind(this));
    // 持续时间
    durationInput.addEventListener("input", this.onDurationInput.bind(this));
    // 播放按钮
    playBtn.addEventListener("click", this.onPlayAnimation.bind(this));
    // 预览按钮
    previewBtn.addEventListener("click", this.onPreviewAnimation.bind(this));
  }

  /**
   * 事件监听相关函数
   * 事件监听相关函数
   * 事件监听相关函数
   * 事件监听相关函数
   * 事件监听相关函数
   */

  // 修改线段宽度
  private lineWidthChange(e: Event) {
    const value = this.checkValue(e);
    if (!value) return;
    this.draw.getCommand().executeUpdateLineOptions({ lineWidth: value });
  }

  // 修改线段颜色
  private lineColorChange(e: Event) {
    const input = <HIE>e.target;
    const color = color_getHexColor(input.value);
    this.draw.getCommand().executeUpdateLineOptions({ lineColor: color });
  }

  // 是否显示线段箭头
  private showPointerChange(e: Event) {
    const input = <HIE>e.target;
    const checked = input.checked;
    this.draw.getCommand().executeUpdateLineOptions({ showPointer: checked });
  }

  // 选择背景填充类型
  private toggleFillType(e: Event) {
    const select = <HSE>e.target;
    const type = select.value;

    const picker = <HE>this.getDom(".file-picker");
    const selector = 'data-operator-input="fill-color"';
    const fillColorInput = <HIE>this.getDom(`[${selector}]`);

    if (type === "image") {
      picker.style.display = "flex";
      fillColorInput.style.display = "none";
    } else {
      picker.style.display = "none";
      fillColorInput.style.display = "block";
    }
  }

  // 图片选择器
  private imagePicker() {
    // 创建 file input
    const input = document.createElement("input");
    input.type = "file";
    input.accept = "image/*";
    input.onchange = async () => {
      // 获取图片文件
      const file = input.files![0];
      // 创建 source 进行预览
      const image = await getImageSource(file);
      const picker = <HE>this.getDom(".file-picker");
      nextTick(() => {
        // 创建预览图
        const img = document.createElement("img");
        img.src = image;
        img.style.width = "100%";
        img.style.height = "100%";
        picker.innerHTML = "";
        picker.appendChild(img);
        input.remove();

        // 真实的添加到 layer 背景图层
        this.draw
          .getLayerManager()
          .setBackgroundLayer({ image, bgType: "image" });

        this.draw.broadcast({ type: "_background", payload: { image } });
      });
    };
    input.click();
  }

  // 颜色输入框变化
  private fillColorInputChange(e: Event) {
    const input = <HIE>e.target;
    this.draw
      .getLayerManager()
      .setBackgroundLayer({ fill: input.value, bgType: "fill" });
    // 背景纯色协同广播
    this.draw.broadcast({
      type: "_background",
      payload: { fill: input.value },
    });
  }

  // 启用网格线
  private toggleGridLine(e: Event) {
    // 颜色取的是
    const target = <HIE>e.target;
    const command = this.draw.getCommand();
    if (target.checked) {
      // 打开网格线
      const color = <HIE>this.getDom('[data-operator-input="gridline-color"]');
      command.executeSetBackground({ grid: true, gridColor: color.value });
    } else {
      // 关闭网格线
      command.executeSetBackground({ grid: false });
    }
  }

  // 网格线颜色
  private gridLineColorChange(e: Event) {
    const input = <HIE>e.target;
    const command = this.draw.getCommand();
    command.executeSetBackground({ gridColor: input.value });
  }

  // 自定义配色
  private zdyColorMatch() {
    const dialog = this.draw.getDialog();
    dialog.open({
      width: 720,
      height: 440,
      title: "自定义配色",
      content: colorMatchTemp,
      onConfirm: async (result) => {
        if (!result) return;
        // 获取用户输入的配色 ,进行自定义设置
        const colorMatch = this.draw.getColorScheme();
        colorMatch.setColorMatch(result);
        // 广播协同配色方案
        this.draw.broadcast({
          type: "_colorMatch",
          payload: result,
        });
      },
    });
  }

  // 应用全部
  private applyAll() {
    this.draw.getLayerManager().setBackgroundLayer({ global: true });
    // 协同应用全部
    this.draw.broadcast({ type: "_background", payload: { global: true } });
    this.draw.getMessage().success("已应用到全部幻灯片");
  }

  // 设置宽度
  private widthChange(e: Event) {
    const value = this.checkValue(e);
    if (!value) return;
    const command = this.draw.getCommand();
    command.executeUpdateShapeOptions({ width: value });
  }

  // 设置高度
  private heightChange(e: Event) {
    const value = this.checkValue(e);
    if (!value) return;
    const command = this.draw.getCommand();
    command.executeUpdateShapeOptions({ height: value });
  }

  // 设置居左
  private leftChange(e: Event) {
    const value = this.checkValue(e);
    if (!value) return;
    const command = this.draw.getCommand();
    command.executeUpdateShapeOptions({ x: value });
  }

  // 设置居顶
  private topChange(e: Event) {
    const value = this.checkValue(e);
    if (!value) return;
    const command = this.draw.getCommand();
    command.executeUpdateShapeOptions({ y: value });
  }

  // 设置填充
  private fillChange(e: Event) {
    const target = <HIE>e.target;
    const value = target.value;
    const command = this.draw.getCommand();
    command.executeUpdateShapeOptions({ fill: value });
  }

  // 设置边框颜色
  private strokeColorChange(e: Event) {
    const target = <HIE>e.target;
    const value = target.value;
    const command = this.draw.getCommand();
    command.executeUpdateShapeOptions({ stroke: value });
  }

  // 设置边框宽度
  private strokeWidthChange(e: Event) {
    const value = this.checkValue(e);
    if (!value) return;
    const command = this.draw.getCommand();
    command.executeUpdateShapeOptions({ strokeWidth: value });
  }

  // 设置文字
  private textChange(e: Event) {
    const target = <HIE>e.target;
    const value = target.value;
    const command = this.draw.getCommand();
    command.executeUpdateShapeOptions({ text: value });
  }

  // 设置文本颜色
  private textColorChange(e: Event) {
    const target = <HIE>e.target;
    const value = target.value;
    const command = this.draw.getCommand();
    command.executeUpdateShapeOptions({ textColor: value });
  }

  // 设置文本大小
  private textFontSizeChange(e: Event) {
    const value = this.checkValue(e);
    if (!value) return;
    const command = this.draw.getCommand();
    command.executeUpdateShapeOptions({ fontSize: value });
  }

  // 设置边框类型
  private strokeTypeChange(e: Event) {
    const target = <HSE>e.target;
    const value = <"dash" | "solid">target.value;
    const command = this.draw.getCommand();
    command.executeUpdateShapeOptions({ strokeType: value });
  }

  // 添加动画按钮点击事件 - 打开动画列表、注册点击事件
  private onAddAnimation() {
    const root = this.draw.getRootBox();

    // 显示动画面板
    const box = root.querySelector(".konva-root-shape-settings");
    const animationListBox = document.createElement("div");
    animationListBox.className = "animation-list";
    animationListBox.innerHTML = animationListTemp;
    box?.appendChild(animationListBox);

    // 对动画类型项进行事件注册
    animationListBox.querySelectorAll("[data-animation]").forEach((item) => {
      const name = <ShapeAnimationEnum>item.getAttribute("data-animation")!;
      item.addEventListener("click", () => this.onItemClick.call(this, name));
    });
  }

  /**
   * 每一个动画子项 点击事件后，需要给当前选中节点添加动画
   * @param name
   * @returns
   */
  private onItemClick(name: ShapeAnimationEnum) {
    const layer = this.draw.getLayer();
    const selected = this.draw.getKonvaGraph().getSelected();
    if (!selected.length || !layer) return;

    const konvaGraph = this.draw.getKonvaGraph();

    // 获取当前动画列表
    const shapeAnimation = this.draw.getShapeAnimation();
    const animationList = shapeAnimation.getAnimationList(layer);

    selected.forEach((group, index) => {
      const order = animationList.length + index;
      const ID = getUniqueId();
      // 给元素添加动画的时候，需要注意顺序
      shapeAnimation.setAnimation({ name, node: group, order, ID });
      // 给元素添加动画标签
      konvaGraph.addAnimationTag(group, order);
    });

    // 记得及时更新 动画列表
    this.updateAnimationList();
  }

  // 触发方式
  private onTriggerSelect(e: Event) {
    console.log(e);
  }

  // 持续时间
  private onDurationInput(e: Event) {
    console.log(e);
  }

  // 播放按钮
  private onPlayAnimation() {}

  // 预览按钮
  private onPreviewAnimation() {}

  /** 辅助函数 - 更新动画列表 */
  private updateAnimationList() {
    const root = this.draw.getRootBox();
    const selector = ".animation-applylist-content";
    const animationListBox = root.querySelector(selector)!;
    animationListBox.innerHTML = "";

    // 获取当前动画列表
    const layer = this.draw.getLayer();
    if (!layer) return;

    // 获取当前动画列表
    const konvaAnimation = this.draw.getShapeAnimation();
    let animationList = konvaAnimation.getAnimationList(layer);
    animationList = animationList.sort((a, b) => a.order - b.order);

    /**
     * 获取动画列表: "click" | "together" | "after";
     *  1. 单击时 xxx
     *  2. 单击时 xxx
     *     wait 在上一个之后 等待上一个结束
     *     together 与上一个一起 不需要等待
     *  3. 拖拽变换顺序时，修改的是 order 字段
     *
     *  解析思路：
     *    1. 找到 order 字段，然后进行排序
     *    2. 使用 curretnOrder 记录当前动画节点
     *    3. 执行当前动画，判断 next 是否是 wait，如果是，则等待上一个动画结束；如果是 together, 则不需要等待
     *    4. 不然 更新 currentOrder 等待下一次 click，依次循环，直到 当前幻灯片 动画列表全部执行完成
     */
    animationList.forEach((an) => {
      const item = document.createElement("div");
      item.className = "animation-list-item";

      const refreshIcon = document.createElement("i");
      refreshIcon.className = "iconfont icon-gengxin1";
      const deleteIcon = document.createElement("i");
      deleteIcon.className = "iconfont icon-shanchu";

      const span = document.createElement("span");
      span.appendChild(refreshIcon);
      span.appendChild(deleteIcon);

      // 添加事件
      refreshIcon.addEventListener("click", () => {
        console.log("refreshIcon");
      });
      deleteIcon.addEventListener("click", () => {
        console.log("deleteIcon");
      });

      item.innerHTML = `${an.order} - 节点_${an.nodeID!.slice(0, 3)} -
      ${ANIMATION_NAME_MAP[an.name]}`;

      item.appendChild(span);

      animationListBox.appendChild(item);
    });
  }

  /** 辅助函数 - 关闭动画列表 */
  private closeAnimationList() {
    const root = this.draw.getRootBox();
    const selector = ".konva-root-shape-settings";
    const selectBox = root.querySelector(selector);
    selectBox?.querySelector(".animation-list")?.remove();
  }

  /** 辅助函数 - 校验值的合法性 */
  private checkValue(e: Event) {
    const target = <HIE>e.target;
    const value = Number(target.value);

    if (!value) return false;

    if (value < 0) {
      target.value = "0";
      this.draw.getMessage().error("值不能小于 0");
      return false;
    }
    return value;
  }

  /** 辅助函数 - querySelector */
  private getDom(selector: string) {
    const root = this.draw.getRootBox();
    const contentSelector = ".konva-root-shape-settings-menu-content";
    const contentBox = <HTMLElement>root.querySelector(contentSelector)!;
    return contentBox.querySelector(selector);
  }

  /** 辅助函数 - querySelectorAll */
  private getDoms(selector: string) {
    const root = this.draw.getRootBox();
    const contentSelector = ".konva-root-shape-settings-menu-content";
    const contentBox = <HTMLElement>root.querySelector(contentSelector)!;
    return contentBox.querySelectorAll(selector);
  }
}
